Go Http包WebServer运行的整个流程与原理

Http包WebServer运行的整个流程与原理

讲解GO web server运行流程,使用如下例子来讲解。进行源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main

import (
"fmt"
"log"
"net/http"
)

func sayHello(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // 解析参数,默认是不会解析的
fmt.Println(r.Form)

fmt.Println("path: ", r.URL.Path)
fmt.Println("schema: ", r.URL.Scheme)

fmt.Println(r.Form["url_long"])

for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("value: ", v)
}

// 给客户端返回结果
fmt.Fprintln(w, "hello rob")
}

func main() {
http.HandleFunc("/", sayHello)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenServer:", err)
}
}

上述程序就构成了一个简单的web server,是如何运行的呢?就从main函数开始讲解。

Handler函数的注册

首先注册我们handler函数

1
http.HandleFunc("/", sayHello)

http.HandleFunc函数的源码如下:

1
2
3
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}

可以看到,http.HandleFunc函数的实质是DefaultServeMux的HandleFunc方法,那么DefaultServeMux的定义为:

1
2
3
4
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

可以看出DefaultServeMux就是ServeMUX 指针类型。ServeMux的作用就是起到了默认的路由器的作用。定义如下:

1
2
3
4
5
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}

其HandleFunc方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}

// 这里调用了自身的Handle方法, HandlerFunc 函数类型
mux.Handle(pattern, HandlerFunc(handler))
}

// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()

if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}

if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

if pattern[0] != '/' {
mux.hosts = true
}
}

注意:在改方法中,handler就是sayHello函数.mux.Handle(pattern, HandlerFunc(handler))中,HandlerFunc(handler) 是一个强制类型转换语句,HandlerFunc其实一个函数类型,实现了Handler接口。定义如下:

1
2
3
4
5
6
7

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r) // 调用HandlerFunc类型自身。
}

从源码中可以看出,HandlerFunc类型实现了http.Handler接口的ServeHTTP函数,也就说HandlerFunc也就是http.Handler类型。其中的 ServeMuxHandle方法,就实现了URI和Handler的映射。

请求是如何到达后端的处理函数

说明请求是如何到达后端函数,从如下语句说起:

1
err := http.ListenAndServe(":8080", nil)

http.ListenAndServe函数,设置Web Server的端口,并监听客户端的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
func ListenAndServe(addr string, handler Handler) error {
// 创建server,我们设置的handler为nil,相当于默认使用ServeMux去处理
server := &Server{Addr: addr, Handler: handler}

// 调用了Server的ListenAndServe()方法
return server.ListenAndServe()
}

// Server的ListenAndServe()方法的源码如下:
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}

// 调用net包中的Listen函数,监听网络
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

// Server 的Serve方法的源码如下
func (srv *Server) Serve(l net.Listener) error {
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}

l = &onceCloseListener{Listener: l}
defer l.Close()

if err := srv.setupHTTP2_Serve(); err != nil {
return err
}

if !srv.trackListener(&l, true) {
return ErrServerClosed
}
defer srv.trackListener(&l, false)

var tempDelay time.Duration // how long to sleep on accept failure
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept() // 阻塞监听客户端的连接
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
// 这里表示,有客户端的请求进来
// 调用Server的newConn方法,获取到一个连接
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx) // 使用goroutines处理我们的请求
}
}


// Create new connection from rwc.
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv, // 将Server实例传入,初始化conn
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}

注意: c 为conn实例,代表HTTP Server端的一个连接,内部封装Server对象信息。

c.serve(ctx)语句就是处理用户请求,其源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
defer func() {
if err := recover(); err != nil && err != ErrAbortHandler {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
}
if !c.hijacked() {
c.close()
c.setState(c.rwc, StateClosed)
}
}()

.....// 省略

// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
// 内部的serverHandler结构体,获取Server对象,创建
// serverHandler实例,并调用ServerHTTP方法
serverHandler{c.server}.ServeHTTP(w, w.req)
.....
}

serverHandler结构体的ServeHTTP方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
// 首先去获取Server的Handler,我们初始化的时候为nil,从这里可以看出
// 我们可以实现Handler来接管默认的路由器
handler := sh.srv.Handler
if handler == nil {
// 如果handler为nil, 就使用DefaultServeMux,也就是默认的路由器
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}

// 调用ServerMux的ServeHTTP方法, 实现路由
handler.ServeHTTP(rw, req)
}

ServerMux的ServeHTTP方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}

// 根据request的URI获取对应的Handler,这里就是我们的HandlerFunc类型的handler
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

// CONNECT requests are not canonicalized.
if r.Method == "CONNECT" {
// If r.URL.Path is /tree and its handler is not registered,
// the /tree -> /tree/ redirect applies to CONNECT requests
// but the path canonicalization does not.
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}

return mux.handler(r.Host, r.URL.Path)
}

// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)

// If the given path is /tree and its handler is not registered,
// redirect for /tree/.
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}

if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}

return mux.handler(host, r.URL.Path)
}

func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()

// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}

当获取到HandlerFunc类型Handler时,就会调用去调用自己,HandlerFunc的ServeHTTP方法如下:

1
2
3
4
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r) // 调用自己,也就是我们注册的后台处理方法。在这个例子中就是调用sayhello函数
}

Go 代码执行流程

通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。
首先调用Http.HandleFunc
按顺序做了几件事:

  • 调用了DefaultServeMux的HandleFunc

  • 调用了DefaultServeMux的Handle

  • 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则

  • 其次调用http.ListenAndServe(“:9090”, nil), 按顺序做了几件事情:

    • 实例化Server

    • 调用Server的ListenAndServe()

    • 调用net.Listen(“tcp”, addr)监听端口

    • 启动一个for循环,在循环体中Accept请求

    • 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()

    • 读取每个请求的内容w, err := c.readRequest()

    • 判断handler是否为空,如果没有设置handler( 这个例子就没有设置handler) ,handler就设置为
      DefaultServeMux

    • 调用handler的ServeHttp

    • 在这个例子中,下面就进入到DefaultServeMux.ServeHttp

    • 根据request选择handler,并且进入到这个handler的ServeHTTP

      1
      mux.handler(r).ServeHTTP(w, r)
    • 选择handler:
      A 判断是否有路由能满足这个request( 循环遍历ServeMux的muxEntry)
      B 如果有路由满足,调用这个路由handler的ServeHTTP
      C 如果没有路由满足,调用NotFoundHandler的ServeHTTP

# Go
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×